Skip to content

✨ Workflow models: safe label garbage collection on rebind and model-defined human-gate replies - #17710

Merged
hivecommons-hive[bot] merged 4 commits into
v5from
workflowmodel/v5-p4-labels-commands
Nov 11, 2026
Merged

hivecommons-hive[bot] merged 4 commits into
v5from
workflowmodel/v5-p4-labels-commands

Conversation

@clubanderson

Copy link
Copy Markdown
Member

Closes two RFC phase-4 gaps of the workflow-models RFC: model labels were never removed once a repository was unbound or rebound, and a bound model's replies (the human-gate verbs it declares per state) were never offered or accepted. P4's "done when … the change regenerates labels and commands" now holds for both halves.

Refs #17620

A. Safe label garbage collection on unbind / rebind

ensureStageLabels already provisions a bound model's LabelDefs. It now remembers what it provisioned and reconciles when the binding changes (pkg/github/automerge/automerge_stage_labels_gc.go):

  • Record — one JSON per repository, workflows/provisioned/<owner>__<repo>.json under data.workflows_dir (workflowmodel.ProvisionedLabels{repo, model, hash, labels[]} via the new ProvisionedStore interface the workflow store implements). It is the hive's memory of the labels it created or adopted for a model.
  • Diff — when the profile hash differs from the record (rebind, new version, unbind → nil/hive-default), compute previous − current.
  • Delete only when all hold: the label is in the record; it is not in the built-in family (stagelabels.LabelDefsFor + Renames); no open issue/PR wears it (label:"x" is:open search — a failed search keeps the label and logs); workflow.label_gc is on. Kept labels stay in the record and are retried next pass. Every deletion is audited workflow_label_removed.
  • Knob — workflow.label_gc (WorkflowConfig.LabelGC *bool, default true). false keeps everything and logs what would have gone. Yaml-only; added to settings_parity_allowlist.txt.
  • Unbound repositories: zero change — no store wired / never bound ⇒ nothing remembered, nothing deleted; the existing stage-label tests are untouched.

B. Model-defined replies → human-gate verbs and /hive <verb>

For a repository bound to a non-default model only:

  • Profile.RepliesForNumber(stage) / ReplyForNumber(stage, verb) → ReplyEffect{Verb, From, To, Label} (the transition whose on event is the verb, its target state, and the label labels.entries maps to that state, if any). NormalizeReply folds case/whitespace/leading slash.
  • Comments — parseUnparkCommandFor tries the model's verbs first (multi-word, longest match, trailing punctuation trimmed; rest of the line is the decision text), then the built-ins. With no verbs it is the old parser. On a parked issue applyIssueModelReply clears parking labels as /hive approve does, applies the mapped label (audited hive_label_applied with reply=<verb>), and posts one comment that keeps the Decision: round-trip intact and says what the model did. Answered replies are watermarks. cmd/hive passes the stage engine's WorkflowFor to the un-park sweep.
  • Dashboard — tracker payload workflows.repos[<repo>].replies (stage number → verbs; hive-default block carries none). stageTrackerHumanGateVerbs appends them after the built-ins, deduped by label, as wf:<verb> actions → prompt for an optional note → POST /api/repos/{repo}/items/{n}/decision {"decision":"model-reply","reply":…}. The handler resolves the bound profile, 409s unbound repos / verbs not offered at the item's state, 400s PRs, audits repo_item_decision_model_reply.

C. Docs

  • docs/design/workflow-models.md — appended "Label GC and model commands (RFC phase 4, follow-up)".
  • docs/maintainer-commands.md — new "Model-defined replies" section.
  • changelog.d/added-workflowmodel-label-gc-commands.md.

Tests

  • automerge: rebind removes stale, keeps in-use, keeps built-in, keeps non-provisioned, honours label_gc=false, persists/updates record, memo resets, search failure keeps label, audit emitted; unbound path unchanged.
  • workflowmodel: NormalizeReply, reply resolution per state, event→label match; store: provisioned record round-trip.
  • github: parser table (bound vs unbound, multi-word, punctuation), verbs by state / hive-default skipped, sweep applies mapped label, unknown verb on unbound repo, operator path ok/refusals/argument errors, reply wording branches.
  • dashboard: payload replies per block, workflowProfileForRepo, handler (unbound 409, blank 400, unknown 409, PR 400, success applies labels + audits), index.html wiring strings, node fixture for stageTrackerWorkflowReplies / stageTrackerHumanGateVerbs / stageTrackerModelReplyVerb.

Coverage: automerge 88.5%, github 88.3% (floor 88), stageengine 94.0% (floor 90), workflowmodel 93.8% (floor 90), config 90.0%. Stage goldens unchanged. pkg/dashboard passes except the two known macOS-only failures.

Known gaps

  • Model replies act on parked issues only: verbs on PR states (e.g. TechDocs draft-review: approve / request changes) are not offered or honoured; PRs keep GitHub's native review. Un-parked states with replies are not answered.
  • The pill shows model verbs only where it already shows a human gate.
  • GC trusts the provisioned record: labels a model dropped before this shipped are never reclaimed automatically.
  • Footprint note: cmd/hive/main_sweeps.go (one option) and api_repo_item_decision.go (one decision case) were touched because the pill needs a server endpoint; no api_workflows*, openapi.json or Workflows-tab JS changes.

@kubestellar-prow kubestellar-prow Bot added the dco-signoff: yes Indicates the PR's author has signed the DCO. label Nov 11, 2026
@kubestellar-prow

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign nicholasjackson for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@kubestellar-prow kubestellar-prow Bot added the size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. label Nov 11, 2026
@github-actions

github-actions Bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

Open PR overlap check

Base branch: v5

Conflicts are checked with every PR merged onto the current tip of v5, so a PR that only needs a rebase is not reported as conflicting with its siblings.

PR Title Relation Files
#17706 ✨ Workflow models: visual editor with live compile, YAML, diff, simulate and export same files src/docs/design/workflow-models.md
src/pkg/dashboard/static/index.html
#17716 ✨ benchmarks: per-PR evidence export + opt-in publish to hivecommons/benchmarks same files src/cmd/hive/main_sweeps.go
src/pkg/config/config.go
src/pkg/dashboard/static/index.html
#17512 🐛 fix(github): recognise the hive's own unpark replies when it posts as an unconfigured login same files src/pkg/github/issue_unpark_command.go
#17714 ✨ Workflow models: signed bundles, hub marketplace catalogue with nightly refresh, spoke import/publish same files src/docs/design/workflow-models.md
src/pkg/config/config.go
src/pkg/dashboard/static/index.html
#17713 ✨ Workflow models: ingest Tekton from a cluster and written specs (LLM-assisted, inferred) same files src/docs/design/workflow-models.md
src/pkg/dashboard/static/index.html

@github-actions github-actions Bot added the conflicts-with-open-pr Open PR has a merge conflict with another open PR label Nov 11, 2026
@hivecommons-hive

hivecommons-hive Bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

Hive status — updated in place; edits do not notify. Full history is in the Activity Monitor.

@clubanderson this PR is held at stage/071-needs-author and the hive will not merge it yet.

Failing checks:

  • relative links and anchors resolve

Next: the hive does not push to your branch. Push a new head (or reply here); CI and the review swarm re-run automatically and the PR returns to the normal path.

Evidence bundle `#17710238d1bb` sha256:ff204c2005a6e4c51711918240d7c5597f4560a02f2da06c98b7ccbbfdac8019 — download from the dashboard or `hivectl review evidence #17710`.

@github-actions

github-actions Bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

Mutation testing

⏱️ The 900s mutation budget ran out, so 2 package(s) were not fully mutated and the results below do not cover them:

  • pkg/stageengine: skipped (not started)
  • pkg/workflowmodel: skipped (not started)

❌ gremlins did not produce a report for 1 package(s), so no verdict can be derived from this run (see the job log):

  • pkg/github: gremlins exited 137

2 mutant(s) in changed files are not reached by any test (NOT COVERED). A maintainer may apply the mutation/acknowledged label to accept this.

File Line Mutation
src/pkg/config/config.go 300 CONDITIONALS_NEGATION
src/pkg/dashboard/api_repo_item_decision.go 560 CONDITIONALS_NEGATION

Verdict: error (#17349).

The stage engine provisions a bound model's label vocabulary but never
removed it: unbinding a repository or rebinding it to a model with a
different label set left the old labels behind forever.

ensureStageLabels now remembers, per repository, which model labels it
provisioned (workflows/provisioned/<owner>__<repo>.json in the workflow
store) and on a hash change or unbind deletes the ones the current
binding no longer declares — only when the label was provisioned by the
hive, is outside the built-in stage family, is worn by no open issue or
PR (one search per label; a failed search keeps it) and workflow.label_gc
(default true) allows. Kept labels stay in the record so a later pass
retries them; every deletion is audited as workflow_label_removed.

A repository that was never bound, or a hive without the workflow store
wired, makes no extra call and keeps the pre-existing behaviour.

Refs #17620

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
A bound workflow model declares `replies` per state — the verbs a human
may answer a gate with. Until now the compiler kept them but nothing
offered or accepted them; the Activity Monitor pill and `/hive`
comments only knew the built-in approve/decision/hold family.

Now, for a repository bound to a non-default model:

- `workflowmodel.Profile.RepliesForNumber` / `ReplyForNumber` resolve
  the verbs for the item's current stage number and the effect of each
  (the transition's `on` event, the target state, and the label
  `labels.entries` maps to that state, if any).
- The `/hive` comment parser (`parseUnparkCommandFor`) tries the model's
  verbs first — multi-word, case-insensitive, trailing punctuation
  trimmed — and the sweep answers a parked issue with
  `applyIssueModelReply`: parking labels cleared as for approve, the
  mapped label applied (audited `hive_label_applied` with the reply),
  and one comment that keeps the `Decision:` round-trip intact and says
  what the model did with the reply. Answered replies are watermarks.
  Repositories with no binding go through exactly the old parser.
- The tracker payload's `workflows.repos[<repo>].replies` carries the
  verbs per stage number; `stageTrackerHumanGateVerbs` appends them
  after the built-ins (deduped by label) as `wf:<verb>` actions, which
  POST `{"decision":"model-reply","reply":…}` to the item decision
  endpoint. The endpoint validates the verb against the bound model and
  audits `repo_item_decision_model_reply`. hive-default's block and the
  pill on unbound repositories are unchanged.
- The issue-unpark sweep in cmd/hive gets the stage engine's
  `WorkflowFor` resolver so comment replies see the same binding the
  labeller does.

Model replies are accepted on parked issues only; PR-state replies are
left for a follow-up.

Refs #17620

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Append "Label GC and model commands (RFC phase 4, follow-up)" to the
design doc — the provisioned record, the safety checks a deletion must
pass, the workflow.label_gc knob, how a model's replies flow through
the comment parser, the un-park sweep and the dashboard pill, and the
gaps left (PR-state replies, un-parked states) — add a "Model-defined
replies" section to maintainer-commands.md, and the changelog fragment.

Refs #17620

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
…h a client.go citation

The label a model reply applies is written because a maintainer answered
the gate — `/hive <verb>` in a comment or the pill on the dashboard — so
it is a solicited write for the writeback ratchet, like the rest of the
un-park path. The RankActionableIssues citation in scale-envelope.md
moved with the RecordWorkflowLabelRemoved helper added above it.

Refs #17620

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
@clubanderson
clubanderson force-pushed the workflowmodel/v5-p4-labels-commands branch from f47c5ff to 238d1bb Compare November 11, 2026 17:37
@kubestellar-prow kubestellar-prow Bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Nov 11, 2026
@hivecommons-hive
hivecommons-hive Bot merged commit 00f2ebd into v5 Nov 11, 2026
84 of 87 checks passed
@kubestellar-prow
kubestellar-prow Bot deleted the workflowmodel/v5-p4-labels-commands branch November 11, 2026 17:43
@hivecommons-hive

Copy link
Copy Markdown
Contributor

merged by Hive trusted-author auto-merge: author @clubanderson holds role owner and GitHub permission admin on repo; CI green at 238d1bb.

@github-actions

Copy link
Copy Markdown
Contributor

Thank you for your contribution! Your PR has been merged.

We'd love to hear how your experience was: share feedback

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dco-signoff: yes Indicates the PR's author has signed the DCO. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant